package org.imraniqbal.pacman;

import java.util.Vector;
import org.imraniqbal.engine.Direction;
import org.imraniqbal.engine.Game;
import org.imraniqbal.engine.GameEvent;
import org.imraniqbal.engine.GameListener;
import org.imraniqbal.engine.Map;
import org.imraniqbal.engine.Mappable;
import org.imraniqbal.engine.Player;

/**
 * @author  imran
 */
public class Ghost extends Player implements GameListener{

	private static final long serialVersionUID = 4549401023722641225L;
	/**
	 * @uml.property  name="iD"
	 */
	private int ID;
	/**
	 * Creates a ghost with a specified ID, direction, postion and lives
	 * @param x X coordinate of the ghost
	 * @param y Y coordinate of the ghost
	 * @param direction Durection ( from Direction enum ) of ghost
	 * @param numOflives number of lives you wish ghost to have
	 * @param iD ID of the ghost, must be unique
	 * @author imran
	 */
	public Ghost(int x, int y, Direction direction, int numOflives, int iD) {
		super(x, y, direction, numOflives);
		this.setID(iD);
	}
	/**
	 * Creates a ghost with specified position and ID
	 * @param x X coordinate of the ghost
	 * @param y Y coordinate of the ghost
	 * @param iD Must be unique, Id of the ghost
	 */
	public Ghost(int x, int y, int iD) {
		super(x, y);
		this.setID(iD);
	}
	/**
	 * Spawns the ghost based on its ID
	 * @author imran
	 */
	@Override
	public void spawn(Map map) {
		setX(7+getID());
		setY(5);
		map.addMappable(this);

	}
	/**
	 * Set ID to specified value
	 * @param iD  Id to be set
	 * @author  imran
	 * @uml.property  name="iD"
	 */
	public void setID(int iD) {
		ID = iD;
	}
	/**
	 * @return  ID of the ghost
	 * @author  imran
	 * @uml.property  name="iD"
	 */
	public int getID() {
		return ID;
	}
	/**
	 * @author imran
	 */
	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof Ghost)){return false;}
		return (super.equals(obj) && ((Ghost)obj).getID() == this.getID());
	}
	/**
	 *  Ghost does necessary action based on event
	 *  @author imran
	 */
	@Override
	public void onEvent(GameEvent e) {
		if( !(e.getSource() instanceof Game)){ return; }
		Game g = (Game) e.getSource();
		Vector<Mappable> m = g.getMap().getMappable(getX(), getY());
		if(m == null){
			return;
		}
		for(int i = 0; i < m.size(); i++){
			Mappable mpl = m.get(i);
			if( mpl instanceof Pacman){
				if(((Pacman)mpl).getState() == PacmanState.BEASTMODE){
					this.die(g.getMap());
					g.setScore(g.getScore() + 50);
				}
			}
		}
		
		//Collision code for tile based games, dealing with things switching spots
		//(this would never be a problem in a non-tile based game)
		
		//universal checking code
		m = g.getMap().getMappable(getOldX(), getOldY());
		if(m == null){ return; }
		for(Mappable mpl : m){
			if( !(mpl instanceof Player) ) { continue; }
			if(((Player)mpl).getOldX() == this.getX() &&
				((Player)mpl).getOldY() == this.getY() ){
				//game specific rule set code below
				if(!(mpl instanceof Pacman)){ continue; }
				if(((Pacman)mpl).getState() == PacmanState.BEASTMODE){
					this.die(g.getMap());
					g.setScore(g.getScore() + 50);
				}
			}
		}
	}
	
	/**
	 * What the fuck is this, i dont even.
	 * @param pacman current instance of Pacman
	 * @param map current instance of Map
	 * @author Colin
	 */
	public void moveGhost(Pacman pacman, Map map){
		//for the purpose of this method, 0 is up, 1 is right, 2 is down, 3 is left
		double uDist = 0;
		double rDist = 0;
		double dDist = 0;
		double lDist = 0;
		
		int difficulty = 1; //This is a sudo difficulty meter, set higher to make ghosts more stupid
		
		int DiscouragingLargeNumber = 1000;
		int SmallerDiscouragingLargeNumber = 990;
		int DiscouragingSmallNumber = -10;
		int LargerDiscouragingLargeNumber = 1;

		//find the distance the ghost is from pacman for each possible move
		for (int i = 0; i < 4; i++){
			switch(i){
			case 0://UP
				if ((map.isWall(this.getX(), this.getY()-1)) == false){
					//not a wall, you can move up
					uDist = Math.sqrt(Math.pow(Math.abs(pacman.getY()-(this.getY()-1)),2) + Math.pow(Math.abs(pacman.getX()-this.getX()),2));
					}else{//there is a wall
						if (pacman.getState() == PacmanState.BEASTMODE){
							uDist = DiscouragingSmallNumber; //set to small number so this will not be chosen as a possible path
						}else{ //NORMAL
							uDist = DiscouragingLargeNumber; //set to large number so when normal, path won't be chosen
						}
					}
				break;
			case 1://RIGHT
				if ((map.isWall(this.getX()+1, this.getY())) == false){
					//not a wall, you can move right
					rDist = Math.sqrt(Math.pow(Math.abs(pacman.getY()-this.getY()),2) + Math.pow(Math.abs(pacman.getX()-(this.getX()+1)),2));
				}else{//there is a wall
					if (pacman.getState() == PacmanState.BEASTMODE){
						rDist = DiscouragingSmallNumber; //set to small number so this will not be chosen as a possible path
					}else{ //NORMAL
						rDist = DiscouragingLargeNumber; //set to large number so when normal, path won't be chosen
					}
				}
				break;
			case 2://DOWN
				if ((map.isWall(this.getX(), this.getY()+1)) == false){
					//not a wall, you can move down
					dDist = Math.sqrt(Math.pow(Math.abs(pacman.getY()-(this.getY()+1)),2) + Math.pow(Math.abs(pacman.getX()-this.getX()),2));
				}else{//there is a wallpacman.getX()-
					if (pacman.getState() == PacmanState.BEASTMODE){
						dDist = DiscouragingSmallNumber; //set to small number so this will not be chosen as a possible path
					}else{ //NORMAL
						dDist = DiscouragingLargeNumber; //set to large number so when normal, path won't be chosen
					}
				}
				break;
			case 3://LEFT
				if ((map.isWall(this.getX()-1, this.getY())) == false){
					//not a wall, you can move left
					lDist = Math.sqrt(Math.pow(Math.abs(pacman.getY()-this.getY()),2) + Math.pow(Math.abs(pacman.getX()-(this.getX()-1)),2));
				}else{//there is a wall
					if (pacman.getState() == PacmanState.BEASTMODE){
						lDist = DiscouragingSmallNumber; //set to small number so this will not be chosen as a possible path
					}else{ //NORMAL
						lDist = DiscouragingLargeNumber; //set to large number so when normal, path won't be chosen
					}
				}
				break;
			}
		}
		// The following switch statement determines the direction you were going, and makes the opposite
		// direction less desirable, since ghosts should not spontaneously change direction
		// The desirability of a direction is based on the state of pacman, since we use large or small
		// numbers to make a direction more or less desirable based on whether pacman is chasing them or
		// running away from them
		switch(this.getDirection()){
		case UP:					
			if (pacman.getState() == PacmanState.BEASTMODE){
				dDist = LargerDiscouragingLargeNumber; 
			}else{ //NORMAL
				dDist = SmallerDiscouragingLargeNumber; }			 
			break;			 
		case RIGHT:
			if (pacman.getState() == PacmanState.BEASTMODE){
				lDist = LargerDiscouragingLargeNumber;
			}else{ //NORMAL
				lDist = SmallerDiscouragingLargeNumber;}
			break;
		case DOWN:
			if (pacman.getState() == PacmanState.BEASTMODE){
				uDist = LargerDiscouragingLargeNumber;
			}else{ //NORMAL
				uDist = SmallerDiscouragingLargeNumber;}
			break;
		case LEFT:
			if (pacman.getState() == PacmanState.BEASTMODE){
				rDist = LargerDiscouragingLargeNumber;
			}else{ //NORMAL
				rDist = SmallerDiscouragingLargeNumber;}
			break;
		}
		// Here, we're adding a small amount of randomness to the distances (a number between 0 and 1)
		// The small amount of randomness prevents situations where distances are exactly the same (hopefully)
		// but is a small enough change to not cause changes in what would be considered the right path to
		// take
		uDist += Math.random()*difficulty;
		rDist += Math.random()*difficulty;
		dDist += Math.random()*difficulty;
		lDist += Math.random()*difficulty;
		
		// By now, the opposite direction the ghost was travelling is less desirable than other paths, but
		// paths that would lead to walls are the least desirable
		if(pacman.getState() == PacmanState.BEASTMODE){//running away
			//go for the farthest away path
			if(uDist > rDist && uDist > dDist && uDist > lDist ){
				this.setDirection(Direction.UP);
			}else if(rDist > uDist && rDist > dDist && rDist > lDist){
				this.setDirection(Direction.RIGHT);
			}else if(dDist > uDist && dDist > rDist && dDist > lDist){
				this.setDirection(Direction.DOWN);
			}else if(lDist > uDist && lDist > rDist && lDist > dDist){
				this.setDirection(Direction.LEFT);
			}
		}else{//chasingLittlePill
			//decide which distance is the shortest to chase
			if(uDist < rDist && uDist < dDist && uDist < lDist ){
				this.setDirection(Direction.UP);
			}else if(rDist < uDist && rDist < dDist && rDist < lDist){
				this.setDirection(Direction.RIGHT);
			}else if(dDist < uDist && dDist < rDist && dDist < lDist){
				this.setDirection(Direction.DOWN);
			}else if(lDist < uDist && lDist < rDist && lDist < dDist){
				this.setDirection(Direction.LEFT);
			}
		}
		this.updateLocation(map); //actually move the ghosts
	}
}
